import os
from typing import List, Dict, Callable

def walk(
    app_path: str,
    callback: Callable[[str, str], List[Dict]],
    ignore_list: List[str] = []
) -> List[Dict]:
    """
    扫描 ThinkPHP 应用目录下的控制器，跳过忽略的路径
    
    :param app_path: ThinkPHP 的 app 目录路径
    :param callback: 处理单个控制器文件的回调函数
    :param ignore_list: 要忽略的文件夹/文件路径列表（支持通配符）
    :return: 权限数据列表
    """
    if ignore_list is None:
        ignore_list = []
    
    permissions = []

    for root, dirs, files in os.walk(app_path):
        # 跳过忽略的文件夹
        if _should_ignore(root, ignore_list):
            continue

        # 只处理 controller 目录
        if not root.endswith('controller'):
            continue

        # 获取应用名 (app 下的子目录名)
        rel_path = os.path.relpath(root, app_path)
        app_name = rel_path.split(os.sep)[0]

        # 处理每个控制器文件
        for file in files:
            file_path = os.path.join(root, file)
            
            # 跳过忽略的文件和非 PHP 文件
            if (not file.endswith('.php') or 
                _should_ignore(file_path, ignore_list)):
                continue

            # 调用回调函数解析控制器
            controller_perms = callback(file_path, app_name)
            permissions.extend(controller_perms)
    
    return permissions

def _should_ignore(path: str, ignore_list: List[str]) -> bool:
    """
    检查路径是否应被忽略
    """
    path = path.replace(os.sep, '/')  # 统一路径分隔符
    for pattern in ignore_list:
        if pattern.endswith('*'):
            # 通配符匹配（如 'cache/*'）
            if path.startswith(pattern[:-1]):
                return True
        elif pattern in path:
            # 完全匹配或子路径匹配
            return True
    return False